home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / editors / mutt / me2s_pl7.zoo / mu_edit2 / ed / led.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-05  |  21.8 KB  |  739 lines

  1. static char rcsid[] = "$Id: led.c,v 1.2 1992/11/10 20:04:22 mike Exp $";
  2.  
  3. /* $Log: led.c,v $
  4.  * Revision 1.2  1992/11/10  20:04:22  mike
  5.  * - Changed according to new version of `Eget_key'.
  6.  *
  7.  * Revision 1.1  1992/09/14  13:02:14  mike
  8.  * Initial revision
  9.  *
  10.  */
  11.  
  12. /* LED.c : Line Editor library
  13.  * External routines:
  14.  *   t_open() : Open terminal.  Probably set Lncol.  If terminal width
  15.  *     changes, you'll need to manage Lvline and Lpline.
  16.  *   t_close() : Close terminal.
  17.  *   t_putchar() : Handles BS, CR and NL.  No echo, ^C trapping.
  18.  *   t_getchar() : No echo.
  19.  *   t_flush() : Flush the terminal.
  20.  *   t_clearline(n) : Put the cursor at the start of a blank line.
  21.  *     n == 0: Put the cursor at the start of a clear line.
  22.  *     n == 1: Move the cursor to start of the line.
  23.  *     Returns: Same as n.
  24.  *     Note: If you can't do n==1, pretend n==0 and return 0;
  25.  * Variables of Interest:
  26.  *   Lncol:  Number columns per line MINUS 1 or 2.
  27.  *   Lpline, Lvline:  pointers to char arrays that hold at least Lncol chars
  28.  *     each.  If Lncol ever changes, make sure the arrays track.
  29.  * Lflags:  Control some internal stuff.
  30.  *   LFnohist : if set, don't insert test into the history stack.  Default
  31.  *     is 0.
  32.  *   LFrecdraw : if set and led recurses, don't redraw the line.  Default is
  33.  *     0.  Used if you want the new prompt to overwrite the current line.
  34.  * Returns:
  35.  *   Led() return code:  Same as Ldo_fcn().
  36.  *   Ldo_fcn() return codes:
  37.  *     LF_DONE:   User said all done now - process my input.  Ltext points
  38.  *       the entered text.  NOTE:  Ltext is only good on LF_DONE.  It may
  39.  *       change from call to call.  It is buffer local.  Text in buffer is
  40.  *       only good till next call to Led() or Ldo_fcn();
  41.  *     LF_ABORT:  User wants to stop - ignore this and get me out.
  42.  *     LF_OK:      Command(s) executed OK.
  43.  *     LF_ERROR:  Something bad happened.  Lerrorno has the error number.
  44.  * Led(prompt,prefload,callback)
  45.  *   callback(kc,n):  When a key is hit that is bound to LF_CALLBACK and
  46.  *     callback is != NULL, callback(kc,n) is called (where kc is the key
  47.  *     pressed and n is what key was bound to).  Return code sez what to do:
  48.  *     return LF_NOOP if nothing is to be done, LF_DONE to simulate the user
  49.  *     pressing LF_DONE, etc.  Normally, just "return Ldo_fcn(...);".
  50.  *   Recursion is OK.
  51.  * Global callbacks:
  52.  *   Lregister_callback(0, (pfi)foo);  foo(kc,n,op) will be called for any
  53.  *     key that is bound to LF_EXTEND.  If foo returns TRUE, no other
  54.  *     callbacks will be called and Ldo_fcn() will be called for what ever
  55.  *     is in op.  If foo return FALSE, the next callback in the list will be
  56.  *     called.
  57.  * Ldo_fcn(list-of-functions-to-do)
  58.  *   Execute a bunch of functions.  The list is terminated be LF_STOP.
  59.  *   For example:
  60.  *     Ldo_fcn(LF_CLEAR_LINE, LF_INSERT_STRING,"hello world", LF_STOP);
  61.  *     This replaces the line with "hello world".
  62.  *     Ldo_fcn(LF_BINDKEY, LF_RIGHT, CTRL|'F', LF_STOP, LF_STOP);
  63.  *       This binds move-cursor-right to control-F.  Note the need for two
  64.  *       LF_STOPs - one to terminate the key binding list and one to
  65.  *       termainate the Ldo_fcn list.
  66.  *   See led.h for list of functions.
  67.  * Ldot : return where the cursor is in Ttext.
  68.  * Lline_length : return the strlen of Ttext.
  69.  * C Durland 1990
  70.  */
  71.  
  72. /* 
  73.  * Other ideas:
  74.  *   A command que for things like signal handlers (^C and the like).
  75.  *   Make sure buffers Line is inited at init time.
  76.  */
  77.  
  78. /* 
  79.  * how to use:
  80.  *   t_open();
  81.  *   [set Lncol, Lvline, Lpline]
  82.  *   init_led();
  83.  *   while(something)
  84.  *   {
  85.  *    Led(....);
  86.  *    do something with results
  87.  *   }
  88.  *   [t_close();]
  89.  *   exit();
  90.  */
  91.  
  92. /* 
  93.  * Implementation notes:
  94.  *   The Line in the Buffer is a bunch of characters with a length.  The
  95.  *     line ALWAYS has enough room at the end for a \0 to turn it into a C
  96.  *     string.
  97.  *   The first thing Led() does is set the prompt.  This will make sure that
  98.  *     the line has space in it (even if prompt is "").
  99.  *   line_is_garbage :
  100.  *     0 : physical line is NOT garbage.
  101.  *     1 : physical line is garbage.
  102.  *     2 : pline is in sync but hardware cursor is out of sync.
  103.  */
  104.  
  105. /* Copyright 1990, 1991 Craig Durland
  106.  *   Distributed under the terms of the GNU General Public License.
  107.  *   Distributed "as is", without warranties of any kind, but comments,
  108.  *     suggestions and bug reports are welcome.
  109.  */
  110.  
  111. static char what[] = "@(#)LED (Line EDitor) v1.0 5/2/90";
  112.  
  113. #include <dtable.h>
  114. #include <const.h>
  115. #include "ed.h"
  116. #include "led.h"
  117.  
  118. #ifdef atarist
  119. #include "../me2/stio.h"
  120. #endif
  121.  
  122. #include <char.h>       /* for iscntrl() only */
  123.  
  124. #ifdef __STDC__
  125.  
  126. #include <stdarg.h>
  127. #define VA_START va_start
  128.  
  129. #else    /* __STDC__ */
  130.  
  131. #include <varargs.h>
  132. #define VA_START(a,b) va_start(a)
  133.  
  134. #endif
  135.  
  136. /* ******************************************************************** */
  137. /* ************* Types and Structures ********************************* */
  138. /* ******************************************************************** */
  139.  
  140. typedef declare_dTable_of(char) Line;
  141. #define LINE_LENGTH    sizeof_dTable(lp)
  142. #define TEXT        (lp->table)
  143.  
  144. typedef struct        /* keys bound to programs */
  145. {
  146.   KeyCode keycode;    /* Key code used to invoke it */
  147.   char fcn;        /* fcn to do */
  148.   short int x;        /* for callbacks, ?? */
  149. } Key;
  150.  
  151. typedef declare_dTable_of(Key) KeyTable;
  152.  
  153. /* ******************************************************************** */
  154. /* ************* Extern Routines ************************************** */
  155. /* ******************************************************************** */
  156.  
  157. extern char *malloc();
  158.  
  159. static int grok_key(), push_line(), pop_line();
  160. static void LEDkeys(), movecursor(), update(), updateline();
  161.  
  162.  
  163. /* ******************************************************************** */
  164. /* ************* Global Variables ************************************* */
  165. /* ******************************************************************** */
  166.  
  167. char
  168.   *Lpline = NULL, *Lvline = NULL,
  169.   *Ltext;        /* the text the user input */
  170. int
  171.   Lerrorno,        /* LEDs error number */
  172.   Lncol,
  173.   Lflags;
  174.  
  175. /* ******************************************************************** */
  176. /* ************* Variables ******************************************** */
  177. /* ******************************************************************** */
  178.  
  179. static int
  180.   lmargin, line_is_garbage, dot, prompt_len,
  181.   hstep, ttcol;
  182. static pfi Lcallback;
  183.  
  184. static KeyCode keypressed;
  185. static Line text = initial_dTable_data(text), *lp = &text;
  186.  
  187. static declare_pkeys(pkeys,SHIFT,SHIFT,SHIFT,SHIFT);
  188.  
  189. static KeyTable gkeys = initial_dTable_data(gkeys);    /* global key table */
  190.  
  191. /* ******************************************************************** */
  192. /* ******************************************************************** */
  193. /* ******************************************************************** */
  194.  
  195. #ifdef __STDC__
  196. int Ldo_fcn(int fcn, ...);
  197. #endif
  198.  
  199.     /* t_open() has been called */
  200. init_Led()
  201. {
  202.   extern void init_linestuff();
  203.  
  204.   if (!Lpline) Lpline = malloc(Lncol);
  205.   if (!Lvline) Lvline = malloc(Lncol);
  206.   hstep = Lncol/3;
  207.  
  208.   LEDkeys();
  209.   Lflags = 0;
  210.   init_linestuff();
  211.   return TRUE;
  212. }
  213.  
  214. #define INSERT   0
  215. #define CALLBACK 1
  216. #define DOFCN     2
  217. #define DOZIP     3
  218.  
  219. Led(prompt,preload,callback)
  220.   char *prompt, *preload;
  221.   pfi callback;
  222. {
  223.   static int rlevel = 0;
  224.  
  225.   int s, f;
  226.   Key *key;
  227.  
  228.   f = (rlevel && (Lflags & LFrecdraw)) ? LF_NOOP : LF_REDRAW;
  229.  
  230.   if (rlevel++) push_line();            /* we are recursing */
  231.   else Htop();                    /* A first time call */
  232.  
  233.   Lcallback = callback;
  234.   
  235.   s = Ldo_fcn(LF_PROMPT,prompt,LF_INSERT_STRING,preload,
  236.       f,LF_UPDATE,LF_STOP);
  237.   while (s != LF_DONE && s != LF_ABORT && s != LF_ERROR)
  238.   {
  239.     key = (Key *)Efind_key(&gkeys,keypressed = Eget_key(pkeys,0L));
  240.     switch (grok_key(key,keypressed))
  241.     {
  242.       case INSERT:
  243.         s = Ldo_fcn(LF_INSERT_KEYCODE,(int)keypressed,LF_UPDATE,LF_STOP);
  244.     break;
  245.       case DOFCN:
  246.         s = Ldo_fcn(key->fcn,LF_UPDATE,LF_STOP);
  247.     break;
  248.       case CALLBACK:
  249.         s = Ldo_fcn(key->fcn,key->x,LF_UPDATE,LF_STOP);
  250.     break;
  251.     }
  252.   }
  253.   if (--rlevel) pop_line();            /* we were recursing */
  254.   return s;
  255. }
  256.  
  257. static int grok_key(key,kc) Key *key; KeyCode kc;
  258. {
  259.   if (key)
  260.   {
  261.     if (key->fcn != LF_CALLBACK && key->fcn != LF_EXTEND) return DOFCN;
  262.     if ((key->fcn == LF_CALLBACK && Lcallback) ||
  263.     (key->fcn == LF_EXTEND))     return CALLBACK;
  264.   }
  265.   if ((kc & 0xFF00) == 0) return INSERT;
  266.   return DOZIP;
  267. }
  268.  
  269. /* ******************************************************************** */
  270. /* ******************************************************************** */
  271. /* ******************************************************************** */
  272.  
  273. static int do_callbacks(), bind_key(), linsert();
  274. static void set_Ltext();
  275.  
  276. /*VARARGS1*/
  277. #ifdef __STDC__
  278. Ldo_fcn(int fcn, ...)
  279. #else
  280. Ldo_fcn(fcn, va_alist) int fcn; va_dcl
  281. #endif
  282. {
  283.   char tmp[20], *ptr;
  284.   register KeyCode kc;
  285.   register int n;
  286.   va_list ap;
  287.  
  288.   VA_START(ap,fcn);
  289.   while (TRUE)
  290.   {
  291.   execute_fcn:
  292.     switch (fcn)
  293.     {
  294.       case LF_NOOP: break;    /* also LF_OK.  no op is a easy thing to do */
  295.       case LF_DONE:               /* done with this line, give it back */
  296.     set_Ltext();
  297.     if (!(Lflags & LFnohist)) Hadd(Ltext);
  298.     alldone:
  299.     va_end(ap);
  300.     return fcn;
  301.       case LF_ERROR:    /* Assumes Lerrorno set.  Useful for callbacks. */
  302.     error: fcn = LF_ERROR; goto alldone;
  303.       case LF_ABORT: goto alldone;
  304.       case LF_STOP: fcn = LF_OK; goto alldone;
  305.  
  306.       case LF_BoL:                    /* begining of line */
  307.     dot = prompt_len;
  308.     lmargin = 0;        /* make sure line updates correctly */
  309.     break;
  310.       case LF_RIGHT:                        /*  right arrow */
  311.     if (dot < LINE_LENGTH) dot++; break;
  312.       case LF_LEFT:                          /* left arrow */
  313.     if (prompt_len<dot) { dot--; if (dot==prompt_len) lmargin = 0; }
  314.     break;
  315.       case LF_EoL: dot = LINE_LENGTH; break;             /* end of line */
  316.  
  317.       case LF_DEL_CHAR:                   /* delete character in place */
  318.     if (dot < LINE_LENGTH)
  319.     {
  320.       for (n = dot; n < LINE_LENGTH; n++) TEXT[n] = TEXT[n+1];
  321.       LINE_LENGTH--;
  322.     }
  323.     break;
  324.       case LF_EEoL: LINE_LENGTH = dot; break;        /* clear to end of line */
  325.       case LF_CLEAR_LINE:               /* clear the entire line */
  326.     Ldo_fcn(LF_BoL,LF_EEoL,LF_STOP);
  327.     break;
  328.       case LF_BS:                    /* destrutive backspace */
  329.     if (prompt_len < dot) Ldo_fcn(LF_LEFT,LF_DEL_CHAR,LF_STOP);
  330.     break;
  331.       case LF_REDRAW: line_is_garbage = 1; break;         /* redraw line */
  332.       case LF_CTYNC:  line_is_garbage = 2; break;         /* sync cursor */
  333.       case LF_UPDATE: update(); break;
  334.       case LF_TAB:                        /* insert a tab */
  335.     Ldo_fcn(LF_INSERT_KEYCODE,'\t',LF_STOP);
  336.     break;
  337.       case LF_QUOTE:                    /* C-Q: quote next char */
  338.     kc = t_getchar();
  339.     Ldo_fcn(LF_INSERT_KEYCODE,kc,LF_STOP);
  340.     break;
  341.       case LF_UQUOTE:                /* \:  UNIX quote next char */
  342.     Ldo_fcn(LF_INSERT_KEYCODE,keypressed,LF_UPDATE,LF_STOP);
  343.     kc = t_getchar();
  344.     if (
  345. ((kc & 0xFF00)==0 && iscntrl(kc)) ||
  346.         Efind_key(&gkeys,Ectok(kc,FALSE))) Ldo_fcn(LF_BS,LF_STOP);
  347.     Ldo_fcn(LF_INSERT_KEYCODE,kc,LF_STOP);
  348.     break;
  349.       case LF_INSERT_KEYCODE:
  350.     kc = va_arg(ap,int /*KeyCode*/);
  351.     *tmp = '\0'; Expand_key(pkeys,kc,tmp); /* make the keycode printable */
  352.     if (!linsert(tmp)) goto error;
  353.     break;
  354.       case LF_INSERT_STRING:
  355.     ptr = va_arg(ap,char *);
  356.     if (!linsert(ptr)) goto error;
  357.     break;
  358.  
  359.       case LF_PROMPT:                    /* set a new prompt */
  360.     ptr = va_arg(ap,char *);
  361.     LINE_LENGTH = dot = 0; if (!linsert(ptr)) goto error;
  362.     prompt_len = strlen(ptr);
  363.     Ldo_fcn(LF_BoL,LF_STOP);
  364.     break;
  365.       case LF_BINDKEY:               /* (bindkey fcn keycode ... LF_STOP) */
  366.     while (TRUE)
  367.     {
  368.       fcn = va_arg(ap,int);
  369.       if (fcn == LF_STOP) break;
  370.       if (fcn==LF_CALLBACK || fcn==LF_EXTEND)   /* (bind callback n kc) */
  371.         n = va_arg(ap,int);
  372.       kc = va_arg(ap,int /*KeyCode*/);
  373.       if (!bind_key(kc,fcn,n)) goto error;
  374.     }
  375.     break;
  376.       case LF_PKEY:                /* (PKEY n keycode ... LF_STOP) */
  377.     while (TRUE)
  378.     {
  379.       n = va_arg(ap,int);
  380.       if (n == LF_STOP) break;
  381.       kc = va_arg(ap,int /*KeyCode*/);
  382.       if (!Eset_pkey(pkeys,n,kc)) { Lerrorno = LEbound; goto error; }
  383.     }
  384.     break;
  385.       case LF_CLEAR_KEYMAP: clear_keytable(&gkeys); break;
  386.       case LF_CALLBACK:                        /* (callback n) */
  387.     fcn = va_arg(ap,int);
  388.     if (!Lcallback) break;
  389.     set_Ltext();
  390.     fcn = (*Lcallback)(keypressed,fcn);
  391.     goto execute_fcn;
  392.       case LF_EXTEND:                          /* (extend n) */
  393.     fcn = va_arg(ap,int);
  394.     fcn = do_callbacks(keypressed,fcn);
  395.     goto execute_fcn;
  396.       default:
  397.         Lerrorno = LEunknown_fcn;
  398.     goto error;
  399.     }
  400.     fcn = va_arg(ap,int);
  401.   }
  402.   /* never gets here */
  403. }
  404.  
  405. static void set_Ltext()
  406. {
  407.   TEXT[LINE_LENGTH] = '\0';        /* know there is room */
  408.   Ltext = &TEXT[prompt_len];
  409. }
  410.  
  411. Ldot()        { return         dot -prompt_len; }
  412. Lline_length()    { return LINE_LENGTH -prompt_len; }
  413.  
  414. /* ******************************************************************** */
  415. /* ************************* Global Callbacks ************************* */
  416. /* ******************************************************************** */
  417.  
  418. #define MAX_CALLBACKS 5
  419.  
  420. static pfi callback_list[MAX_CALLBACKS];
  421. static int num_callbacks = 0;
  422.  
  423. Lregister_callback(callback_num, callback) pfi callback;
  424. {
  425.   if (num_callbacks == MAX_CALLBACKS) return FALSE;
  426.  
  427.   callback_list[num_callbacks++] = callback;
  428.  
  429.   return TRUE;
  430. }
  431.  
  432.     /* Call the callbacks in reverse order so that the last registered will
  433.      *   have the first crack at the fcn.
  434.      */
  435. static int do_callbacks(kc, fcn) KeyCode kc; int fcn;
  436. {
  437.   int j, op;
  438.  
  439.   set_Ltext();
  440.   for (j = num_callbacks; j--; )
  441.     if ( (*callback_list[j])(kc, fcn, &op)) return op;
  442.  
  443.   return LF_NOOP;    /* nobody wanted the callback */
  444. }
  445.  
  446. /* ******************************************************************** */
  447. /* *************** Keys *********************************************** */
  448. /* ******************************************************************** */
  449.  
  450. static void LEDkeys()
  451. {
  452.   Ldo_fcn(LF_CLEAR_KEYMAP,LF_BINDKEY,
  453.     LF_BS,        CTRL|'H',    /* backspace */
  454.     LF_TAB,        CTRL|'I',    /* tab */
  455.     LF_DEL_CHAR,    CTRL|'?',    /* DEL(7F) == ^? == ^D */
  456.     LF_ABORT,        CTRL|'C',    /* exit */
  457.     LF_DONE,        CTRL|'M',    /* enter or CR */
  458.     LF_DONE,        CTRL|'J',    /* newline */
  459.     LF_UQUOTE,        '\\',        /* UNIX style quote */
  460.     LF_REDRAW,        CTRL|'L',    /* refresh-screen */
  461.     LF_CLEAR_LINE,    CTRL|'U',
  462.  
  463.           /* softkeys */
  464.     LF_RIGHT,        SOFKEY|'E',    /* next-character : right arrow */
  465.     LF_LEFT,        SOFKEY|'F',    /* previous-character : left arrow */
  466.     LF_DEL_CHAR,    SOFKEY|'H',    /* delete-char : delete */
  467.  
  468.     LF_STOP, LF_STOP);
  469. }
  470.  
  471. static int bind_key(keycode,fcn,x) KeyCode keycode; int fcn, x;
  472. {
  473.   Key *key;
  474.  
  475.   if (fcn == LF_UNDEF) { Eunbind_key(&gkeys,keycode); return TRUE; }
  476.   if (!(key = (Key *)Ekalloc(&gkeys,keycode)))
  477.     { Lerrorno = LEmem; return FALSE; }
  478.   key->keycode = keycode; key->fcn = fcn; key->x = x;
  479.   return TRUE;
  480. }
  481.  
  482. /* ******************************************************************** */
  483. /* ******************* Allocate Lines ********************************* */
  484. /* ******************************************************************** */
  485.  
  486. #define LINES 10
  487. typedef struct
  488. {
  489.   int lmargin, dot, prompt_len;
  490.   pfi Lcallback;
  491.   Line line, *lp;
  492. } LineStuff;
  493.  
  494. static LineStuff lines[LINES];
  495. static int nth_line = 0;
  496.  
  497. void init_linestuff()
  498. {
  499.   int j;
  500.  
  501.   for (j = LINES; j--; ) INIT_dTable(&lines[j].line);
  502. }
  503.  
  504.    /* save context of current line and set up a new line
  505.     * New line inited by Led().
  506.     * !!!!!!!!!!!assumes that lines[] is initied!!!!!!!!!!!!!!!!
  507.     * Can't init line because it might have been used before and have
  508.     *   allocated some data.
  509.     */
  510. static int push_line()
  511. {
  512.   LineStuff *lsp;
  513.  
  514.   if (nth_line == LINES) return FALSE;
  515.   lsp = &lines[nth_line++];
  516.  
  517.   lsp->lmargin = lmargin;
  518.   lsp->dot = dot;
  519.   lsp->prompt_len = prompt_len;
  520.   lsp->Lcallback = Lcallback;
  521.   lsp->lp = lp;
  522.  
  523.   lp = &lsp->line;
  524.   reset_dTable(lp);
  525.   return TRUE;
  526. }
  527.  
  528.    /* restore last used line */
  529. static int pop_line()
  530. {
  531.   LineStuff *lsp;
  532.  
  533.   lsp = &lines[--nth_line];
  534.  
  535.   lmargin = lsp->lmargin;
  536.   dot = lsp->dot;
  537.   prompt_len = lsp->prompt_len;
  538.   Lcallback = lsp->Lcallback;
  539.   lp = lsp->lp;
  540.   return TRUE;
  541. }
  542.  
  543. /* ******************************************************************** */
  544. /* **************** Manage Contents of Lines ************************** */
  545. /* ******************************************************************** */
  546.  
  547.    /* blkmovm(to,from,n)
  548.     *   F = source, T = dest
  549.     *   |---------**from*F----------|
  550.     *    |---------------***to**T----|
  551.     */
  552.  
  553.  
  554.    /* Open a hole of size n @ dot in a line. */
  555. static void hole(line,dot,n) Line *line; int dot, n;
  556. {
  557.   register char *ptr, *qtr;
  558.   register int x;
  559.  
  560.   qtr = &line->table[sizeof_dTable(line) -1];
  561.   ptr = qtr +n;
  562.   x = sizeof_dTable(line) -dot;
  563.   while (x--) *ptr-- = *qtr--;
  564. }
  565.  
  566. static char deadline[1] = { '\0' };
  567.  
  568. static int linsert(text) char *text;
  569. {
  570.   int n = strlen(text), a = sizeof_dTable(lp);
  571.  
  572.   if (!xpand_dTable(lp,n+1,32,32))
  573.     { lp->table = deadline; Lerrorno = LEmem; return FALSE; }
  574.   sizeof_dTable(lp) = a; hole(lp,dot,n); sizeof_dTable(lp) = a+n;
  575.   memcpy(&lp->table[dot], text, n);
  576.   dot += n;
  577.   return TRUE;
  578. }
  579.  
  580. static int getccol(text,dot) register char *text;
  581. {
  582.   register char c;
  583.   register int i, col = 0;
  584.  
  585.   for (i = dot; i--;)
  586.   {
  587.     c = *text++;
  588.     if (c == '\t') col = NEXT_TAB_COLUMN_MINUS_1(col);
  589.     else if (iscntrl(c)) ++col;
  590.     ++col;
  591.   }
  592.   return col;
  593. }
  594.  
  595. /* ******************************************************************** */
  596. /* ******************* Video routines ********************************* */
  597. /* ******************************************************************** */
  598.  
  599. /* Format a screen line:  convert tabs to spaces, control characters to
  600.  *   printable.  Right pad with blanks.  Shift text left as needed to fit it
  601.  *   between the screen margins.  You have to look at the text from the
  602.  *   beginning to ensure correct tab and control character expansion.
  603.  */
  604. #define ADD_CHAR(c) if (lmargin <= col++) *qtr++ = c
  605. static void vblast(ptr,len,lmargin) unsigned char *ptr; register int lmargin;
  606. {
  607.   register unsigned char *qtr = (unsigned char *)Lvline;
  608.   register int col = 0;
  609.   register unsigned char c;
  610.   register int rmargin = lmargin +Lncol;
  611.  
  612.   while (col < rmargin && len--)
  613.   {
  614.     c = *ptr++;
  615.     if (iscntrl(c))
  616.     {
  617.       if (c == '\t')        /* expand tab */
  618.       {
  619.     register int n = imin(rmargin, NEXT_TAB_COLUMN(col));
  620.  
  621.     do { ADD_CHAR(' '); } while (col < n);
  622.     continue;
  623.       }
  624.       ADD_CHAR('^');        /* LineFeed => "^J" */
  625.       c ^= 0x40;        /* Convert ASCII LineFeed to "J" */
  626.       if (col == rmargin) continue;    /* no room for the "J" */
  627.       /* fall though and put the "J" in the video */
  628.     }
  629.     ADD_CHAR(c);
  630.   }
  631.   if (0 < len) *(qtr-1) = '$';    /* hit rmargin before wrote all of row */
  632.   else                /* clear to end of line */
  633.     memset(qtr,' ',(col <= lmargin) ? Lncol : rmargin -col);
  634.  
  635.   if (0 < lmargin) Lvline[0] = '$';
  636. }
  637.  
  638. /*
  639.  * Make sure that the display is right.  This is a three part process:
  640.  *   First, scan through all of the windows looking for dirty ones.  Check
  641.  *     the framing, and refresh the screen.
  642.  *   Second, make sure that "currow" and "curcol" are correct for the
  643.  *     current window.
  644.  *   Third, make the virtual and physical screens the same.
  645.  */
  646. static void update()
  647. {
  648.   register int j, k, curcol;
  649.  
  650.   curcol = getccol(TEXT,dot);    /* find the cursor column */
  651.   if (hstep!=0)    /* check to see if have to horizontal scroll */
  652.   {
  653.     k = lmargin;
  654.     if (curcol < k)    /* cursor is left of window margin */
  655.     {
  656.       j = ((k -curcol +hstep -1)/hstep)*hstep;
  657.       if ((k -= j)<0) k = 0;
  658.     }
  659.     else
  660.       if (curcol >= k+Lncol)    /* cursor is right of window */
  661.       {
  662.     j = ((curcol -(k +Lncol) +hstep)/hstep)*hstep;
  663.     k += j;
  664.       }
  665.     lmargin = k;
  666.   }
  667.  
  668.   vblast(TEXT,LINE_LENGTH,lmargin);
  669.  
  670.   /* Special hacking if the screen is garbage.  Clear the hardware screen,
  671.    *   and update your copy to agree with it.  Set all the virtual screen
  672.    *   change bits, to force a full update.
  673.    */
  674.   if (line_is_garbage)
  675.   {
  676.     j = t_clearline(line_is_garbage-1);
  677.     if (j == 0) memset(Lpline,' ',Lncol);
  678.     line_is_garbage = 0;
  679.     ttcol = 0;
  680.   }
  681.  
  682.   updateline(Lvline,Lpline);
  683.  
  684.     /* Finally, update the hardware cursor and flush out buffers */
  685.   j = curcol -lmargin;
  686.   if (Lncol <= j) j = Lncol-1;    /* cursor off right side of screen */
  687.   else if (j < 0) j = 0;    /* cursor off left side of screen */
  688.   movecursor(j);
  689.   t_flush();
  690. }
  691.  
  692. /*
  693.  * Update a single line.  This does not know how to use insert or delete
  694.  *   character sequences; we are using Backspace functionality.  Update the
  695.  *   physical row and column variables.
  696.  */
  697. static void updateline(vline,pline) char *vline, *pline;
  698. {
  699.   register char
  700.     *cp1 = vline, *cp2 = pline, *cp4,
  701.     *cp3 = &vline[Lncol], *cp5 = cp3;
  702.  
  703.         /* Compute left match */
  704.   while (cp1 != cp5 && *cp1 == *cp2) { ++cp1; ++cp2; }
  705.  
  706.   if (cp1 == cp5) return;        /* lines are equal */
  707.  
  708.     /* Compute right match */
  709.   cp4 = &pline[Lncol];
  710.   while (cp3[-1] == cp4[-1]) { --cp3; --cp4; }
  711.  
  712.   cp5 = cp3;
  713.   movecursor(cp1-vline);
  714.   while (cp1 != cp5)            /* write out mismatched part */
  715.     { t_putchar(*cp1); ++ttcol; *cp2++ = *cp1++; }
  716. }
  717.  
  718. #define BS 0x08        /* BackSpace */
  719.  
  720. /*
  721.  * Send a command to the terminal to move the hardware cursor to
  722.  *   (row,column).  Can only use BS and write;
  723.  * The row and column arguments are origin 0.
  724.  * Optimize out random calls.
  725.  * Update "ttcol".
  726.  */
  727. static void movecursor(col) register int col;
  728. {
  729.   register char *ptr;
  730.  
  731.         /* move cursor left with backspace */
  732.   while (col < ttcol) { ttcol--; t_putchar(BS); }
  733.   if (ttcol < col)    /* move cursor right with write */
  734.   {
  735.     ptr = &Lvline[ttcol];
  736.     while (ttcol < col) { ttcol++; t_putchar(*ptr++); }
  737.   }
  738. }
  739.